/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "StdAfx.h"
#include "IModelBaseRenderer.h"
#include "IMesh.h"

#include <algorithm>

namespace model {

IModelBaseRenderer::mat_list  IModelBaseRenderer::s_lMaterials;
IModelBaseRenderer::mesh_list IModelBaseRenderer::s_lMeshes;

IModelBaseRenderer::IModelBaseRenderer(void)
{
}

IModelBaseRenderer::~IModelBaseRenderer(void)
{
}


void IModelBaseRenderer::ResetMaterialCount()
{
	for (mat_list::iterator matIt = s_lMaterials.begin(); matIt != s_lMaterials.end(); ++matIt) {
		matIt->uiVisibleMeshes = 0;
	}
}

void IModelBaseRenderer::DrawByMaterial(math::Matrix4 *postMat, bool drawTransparent)
{
	/*
	static int visMeshes;
	static sComplexMaterial *mat;
	static int i;
	static model::BoundingSphere bb;
	static math::Vector3 camPos;
	static std::list<ILight*> *s_pLights = coreGetScene()->getVisibleLights();
	static std::vector<model::IMesh*>* meshlist;
	
	// Draw children
 	for (mat_list::iterator matIt = m_lMaterials.begin(); matIt != m_lMaterials.end(); ++matIt) {
		mat = &(*matIt);

		if (drawTransparent) {
			std::sort(mat->m_lLinkedTranspMeshes.begin(), mat->m_lLinkedTranspMeshes.end(), LessByPointedToValue<IMesh>() );
			meshlist = &(mat->m_lLinkedTranspMeshes);
		} else {
			meshlist = &(mat->m_lLinkedMeshes);
		}

		camPos = coreGetRenderer()->getCurrentCamera()->getPosition();

		if (mat->uiVisibleMeshes > 0) 
		{
			if (s_bDepth) {
				// Draw the depth pass
 				mat->pEffect0->setTechnique(mat->hDepthTech);
				if (mat->pEffect0->bind()) {
					if (mat->isTAMat) {
						// Let's hope, that only the diffuse texture will be used inside the depth technique
						if (mat->hDiffuseTex)
							mat->pEffect0->setTexture(mat->hDiffuseTex, mat->pTexture0);
					} else {
						// Set some uniform parameters for the textures
						if (mat->hDiffuseTex)
							mat->pEffect0->setTexture(mat->hDiffuseTex, mat->pTexture0);
					}

					if ( mat->pEffect0->beginPass(0) ) {
						for (std::vector<IMesh*>::iterator meshIt = meshlist->begin(); meshIt != meshlist->end(); ++meshIt) {
							(*meshIt)->draw(postMat);
						}
						mat->pEffect0->endPass();
					}
					mat->pEffect0->unbind();
				}
			} else {
				/// Set the right technique
				if (mat->isTAMat) 
 					mat->pEffect0->setTechnique(mat->hTATech);
				else              
					mat->pEffect0->setTechnique(mat->hDefaultTech);
			
				/// Bind the effect
				if (mat->pEffect0->bind()) {
					if (mat->isTAMat) {
						if (mat->hDiffuseTex)
							mat->pEffect0->setTexture(mat->hDiffuseTex, mat->pTexture0);
						if (mat->hNormalTex)
							mat->pEffect0->setTexture(mat->hNormalTex, NULL);
						if (mat->hAdditionalTex)
							mat->pEffect0->setTexture(mat->hAdditionalTex, NULL);
					} else {
						// Set some uniform parameters for the textures
						if (mat->hDiffuseTex)
							mat->pEffect0->setTexture(mat->hDiffuseTex, mat->pTexture0);
						if (mat->hNormalTex)
							mat->pEffect0->setTexture(mat->hNormalTex, mat->pTexture1);
						if (mat->hAdditionalTex)
							mat->pEffect0->setTexture(mat->hAdditionalTex, mat->pTexture2);
					}

					// Lights are set per object. With large objects the range may be wrong
					static std::list<ILight*>::iterator it;
					static sShaderLight light;
					
					if ( mat->pEffect0->beginPass(0) ) {
						for (std::vector<IMesh*>::iterator meshIt = meshlist->begin(); meshIt != meshlist->end(); ++meshIt) {
							(*meshIt)->draw(postMat);
						}
						mat->pEffect0->endPass();
					}
					
					if (mat->hLight) {						
						if ( mat->pEffect0->beginPass(1) ) {
							for (std::vector<IMesh*>::iterator meshIt = meshlist->begin(); meshIt != meshlist->end(); ++meshIt) {
								// TODO Check if the light influences the mesh
								bb = model::BoundingSphere((*meshIt)->getBoundingSphere()->getCenter() * (*meshIt)->getWorldTransformMatrix(), (*meshIt)->getBoundingSphere()->getRadius());

								for ( it = s_pLights->begin(); it != s_pLights->end(); ++it ) {
									
									if ((*it)->getLightType() == LIGHT_DIRECTIONAL)
										light.position = (*it)->getDirection();
									else
										light.position = (*it)->getPosition();
									
									light.diffuseColor = (*it)->getDiffuseColor();
									light.ambientColor = (*it)->getAmbientColor();
									light.specularColor = (*it)->getSpecularColor();
									light.attenuation = (*it)->getRange();
									light.lightType = (*it)->getLightType();

									mat->pEffect0->setData(mat->hLight, &light, sizeof(sShaderLight));

									// Is the bounding sphere not intersecting or is the range unlimited?
									if ( (*it)->getRange().x < 0.0f || (*it)->getBoundingSphere()->isIntersecting(bb) ) {
										(*meshIt)->draw(postMat);
									}

									// Break if a posMat is set. Only one light will be rendered
									if (postMat)
										break;
								}
							}
							mat->pEffect0->endPass();
						}
					}
					mat->pEffect0->unbind();
				}
			}
		}
	}
	*/
}

void IModelBaseRenderer::ClearMaterials()
{
	/*
	for (mat_list::iterator matIt = m_lMaterials.begin(); matIt != m_lMaterials.end(); ++matIt) {
		(*matIt).m_lLinkedMeshes.clear();
		(*matIt).m_lLinkedTranspMeshes.clear();
	}
	*/
}

void IModelBaseRenderer::AddMesh(IMesh* mesh)
{
	s_lMeshes.push_back(mesh);
}

void IModelBaseRenderer::RemoveMesh(IMesh* mesh)
{
	mesh_list::iterator it = std::find(s_lMeshes.begin(), s_lMeshes.end(), mesh);
	if (it != s_lMeshes.end()) 
		s_lMeshes.remove(*it);
}

void IModelBaseRenderer::DrawByMesh(math::Matrix4 *postMat, bool drawTransparent)
{
	// Go through all the meshes and draw it according to its transparency value
	for (mesh_list::iterator it = s_lMeshes.begin(); it != s_lMeshes.end(); ++it) {
		if ( (*it)->isTransparent() == drawTransparent)
			(*it)->drawNodes(drawTransparent, postMat);
	}
}

} // namespace
